﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Threading;
using System.Timers;
using System.IO;
using System.Text.RegularExpressions;
using System.Security.Permissions;
using System.Reflection;
using System.Windows.Media;
using System.Windows.Forms;

using Styx;
using Styx.Common;
using Styx.CommonBot;
using Styx.CommonBot.Inventory;
using Styx.CommonBot.POI;
using Styx.CommonBot.Routines;
using Styx.Helpers;
using Styx.Pathing;
using Styx.WoWInternals;
using Styx.WoWInternals.WoWObjects;

using UltraFTW.data.libs;
using UltraFTW.data.libs.util;

namespace UltraFTW.data.libs.core
{
    public class FTWCore
    {
        public static Dictionary<ulong, Dictionary<String, double>> properties;

        public void Initialize()
        {
            Pulse();
            FTWCoreStatus.On_DumpParty();
            EvaluateRules("@INITIALIZE");
        }

        public void Pulse()
        {
            string fnname = "FTWCore.Pulse";
            MyTimer.Start(fnname);
            try
            {

                if (!FTWUtils.ValidState())
                {
                    // Do nothing
                    FTWLogger.log(Colors.Red, "Not valid state");
                }
                else
                {
                    // Get lists of interesting people

                    // Party or raid members
                    FTWProps.people = FTWCoreUnits.GetGroup();

                    // Tank
                    FTWProps.tank = FTWCoreUnits.GetTank();

                    // Healer
                    FTWProps.healer = FTWCoreUnits.GetHealer();

                    // Party members within range
                    FTWProps.closePeople = FTWProps.people.Where(p => p.DistanceCalc() <= 40).ToList();

                    // Party members within range, in line of sight, that need healing
                    FTWProps.healTarget = FTWCoreUnits.GetHealTarget();

                    // Diseased party member
                    // Only tanks and me get dispels, the rest can stay sick.
                    // A necessary optimization to keep the speed up.
                    FTWProps.cleanseTarget = (from unit in FTWProps.closePeople
                                     where unit.Role().Contains("Tank") || unit.Guid == StyxWoW.Me.Guid
                                     where unit.EligibleForCleanse() == true
                                     orderby unit.HealWeight()
                                     select unit).FirstOrDefault();

                    // Dead party member outside combat
                    FTWProps.reviveTarget = (from unit in FTWProps.closePeople
                                    where unit.IsDead || unit.IsGhost
                                    select unit).FirstOrDefault();
                    if (FTWProps.reviveTarget != null)
                        FTWLogger.log(Colors.Red, "{0} is dead - revive him if you can.", FTWProps.reviveTarget.SafeName());

                    // Get average health of all party members.
                    try
                    {
                        FTWProps.avgHealth = (int)(from p in FTWProps.closePeople where p.IsDead == false select p.HealthPercent).Average();
                    }
                    catch (Exception ex)
                    {
                        FTWProps.avgHealth = (int)StyxWoW.Me.HealthPercent;
                    }

                    // Get mobs fighting us
                    FTWProps.adds = FTWCoreUnits.GetAdds();

                    // Get mobs fighting us, that we're not targeting
                    WoWUnit pettarget = null;
                    if (StyxWoW.Me.GotAlivePet)
                        pettarget = StyxWoW.Me.Pet.CurrentTarget;
                    FTWProps.nontargetadds = (from unit in FTWProps.adds
                                     where unit != StyxWoW.Me.CurrentTarget && unit != pettarget
                                     select unit).ToList();

                    // Get a mob fighting us, that we're not targeting.
                    FTWProps.add = FTWProps.nontargetadds.FirstOrDefault();
                }
            }
            catch (Exception ex)
            {
                FTWLogger.log(Colors.Red, ex.ToString());

            }
            MyTimer.Stop(fnname);
        }
        
        public bool EvaluateRules(String section)
        {
            bool retval = false;


            string fnname = string.Format("FTWCore.EvaluateRules {0}", section);
            MyTimer.Start(fnname);
            MyTimer.Start("FTWCore.EvaluateRules.Remainder");            
            
            if(!FTWProps.rule_defs.ContainsKey(FTWProps.ClassAndSpec))
                FTWLogger.debug("Missing class = " + FTWProps.ClassAndSpec);
            else if (!FTWProps.rule_defs[FTWProps.ClassAndSpec].ContainsKey(section))
                FTWLogger.debug("Missing section = " + section);
            else
            {
                foreach (string action in FTWProps.rule_defs[FTWProps.ClassAndSpec][section].Keys)
                {
                    bool ConditionsMatch = false;
                    bool HasConditions = false;
                    foreach (List<FTWCondition> cond_blk in FTWProps.rule_defs[FTWProps.ClassAndSpec][section][action])
                    {
                        bool CondBlockMatch = true;
                        foreach (FTWCondition cond in cond_blk)
                        {
                            HasConditions = true;
                            if (CondBlockMatch)
                                CondBlockMatch = EvaluateCondition(cond);
                        }
                        if (CondBlockMatch)
                            ConditionsMatch = true;
                    }

                    if (ConditionsMatch || !HasConditions)
                    {
                        if (FTWProps.action_index[action].Execute())
                        {
                            FTWLogger.debug("Action " + action + " SUCCEEDED");
                            retval = true;
                        }
                        else
                            FTWLogger.debug(Colors.Red, action + " FAILED");
                    }
                    else
                        FTWLogger.debug(Colors.Red,action + " SKIPPED due to failed conditions");
                }
            }
            MyTimer.Stop("FTWCore.EvaluateRules.Remainder");
            MyTimer.Stop(fnname);
            return retval;
        }

        private bool EvaluateCondition(FTWCondition cond)
        {
            bool retval = false;

            double cur_val = 0.0;
            bool val_found = false;
            if (cond.type == "BASIC")
            {
                val_found = true;
                cur_val = GetBasicProperty(cond.target, cond.prop);
            }
            else if (cond.type == "ADVANCED")
            {
                val_found = true;
                cur_val = GetAdvancedProperty(cond.target, cond.aura_action, cond.aura);
            }
            
            if (val_found)
            {
                if (cond.op == "=")
                {
                    if (cur_val == cond.value)
                        retval = true;
                }
                else if (cond.op == "!=" || cond.op == "<>")
                {
                    if (cur_val != cond.value)
                        retval = true;
                }
                else if (cond.op == ">=")
                {
                    if (cur_val >= cond.value)
                        retval = true;
                }
                else if (cond.op == "<=")
                {
                    if (cur_val <= cond.value)
                        retval = true;
                }
                else if (cond.op == ">")
                {
                    if (cur_val > cond.value)
                        retval = true;
                }
                else if (cond.op == "<")
                {
                    if (cur_val < cond.value)
                        retval = true;
                }
            }

            return retval;
        }

        public double GetBasicProperty(WoWUnit unit, string name)
        {
            ulong guid;
            if (unit != null)
                guid = unit.Guid;
            else
                guid = 0;
            Dictionary<string, double> values;
            double value = 0;
            if (!properties.ContainsKey(guid))
            {
                values = new Dictionary<string, double>();
                properties[guid] = values;
            }
            else
            {
                values = properties[guid];
            }
            if (!values.ContainsKey(name))
            {
                value = GetBasicPropValue(unit, name);
                values[name] = value;
            }
            else
            {
                value = values[name];
            }
            return value;
        }

        private double GetBasicPropValue(WoWUnit theunit, string name)
        {
            double value = 0;
            switch (name)
            {
                case "IsHostile": value = theunit != null && theunit.IsHostile ? 1 : 0; break;
                case "AvgHealth": value = FTWProps.avgHealth; break;
                case "AddsCount": value = FTWProps.adds.Count; break;
                case "StunCount": value = theunit != null ? FTWCoreStatus.StunCount(theunit.Location) : 0; break;
                case "Aggro": value = theunit != null ? (int)theunit.ThreatInfo.ThreatStatus : 5; break;
                case "MovementDisabled": value = theunit != null && FTWUtils.MovementDisabled() ? 1 : 0; break;
                case "ClusterSize": value = theunit != null ? theunit.ClusterSize() : 0; break;
                case "CapsLock": value = FTWUtils.CapsLockDown() ? 1 : 0; break;
                case "BearForm": value = theunit != null && theunit.HasAura("Bear Form") ? 1 : 0; break;
                case "CatForm": value = theunit != null && theunit.HasAura("Cat Form") ? 1 : 0; break;
                case "MoonkinForm": value = theunit != null && theunit.HasAura("Moonkin Form") ? 1 : 0; break;
                case "NormalForm": value = theunit != null && (theunit.HasAura("Cat Form") == false && theunit.HasAura("Bear Form") == false && theunit.HasAura("Moonkin Form") == false) ? 1 : 0; break;
                case "FlightForm": value = theunit != null && (theunit.HasAura("Flight Form") || theunit.HasAura("Swift Flight Form")) ? 1 : 0; break;
                case "FeralForm": value = theunit != null && (theunit.HasAura("Bear Form") || theunit.HasAura("Cat Form")) ? 1 : 0; break;
                case "ComboPoints": value = StyxWoW.Me.ComboPoints; break;
                case "AllComboPoints": value = StyxWoW.Me.RawComboPoints; break;
                case "Rage": value = StyxWoW.Me.RageInfo.CurrentI; break;
                case "Focus": value = StyxWoW.Me.FocusInfo.CurrentI; break;
                case "Energy": value = StyxWoW.Me.EnergyInfo.CurrentI; break;
                case "ShadowOrbs": value = StyxWoW.Me.GetPowerInfo(WoWPowerType.ShadowOrbs).CurrentI; break;
                case "SoulShards": value = StyxWoW.Me.SoulShardsInfo.CurrentI; break;
                case "Balance": value = StyxWoW.Me.UnitPower(8); break;
                case "HolyPower": value = StyxWoW.Me.GetPowerInfo(WoWPowerType.HolyPower).CurrentI; break;
                case "Chi": value = StyxWoW.Me.UnitPower(12); break;
                case "BurningEmbers": value = StyxWoW.Me.GetPowerInfo(WoWPowerType.BurningEmbers).CurrentI; break;
                case "DemonicFury": value = StyxWoW.Me.GetPowerInfo(WoWPowerType.DemonicFury).CurrentI; break;
                case "EclipseDirection": value = FTWProps._eclipsedirection; break;
                case "Distance": value = theunit != null ? theunit.Distance2D : 0; break;
                case "MeleeRange": value = 4.95; break;
                case "IsTank": value = StyxWoW.Me.IsTank() ? 1 : 0; break;
                case "IsHealer": value = StyxWoW.Me.IsHealer() ? 1 : 0; break;
                case "IsDPS": value = StyxWoW.Me.IsDPS() ? 1 : 0; break;
                case "IsDemon": value = theunit != null && theunit.CreatureType == WoWCreatureType.Demon ? 1 : 0; break;
                case "IsElemental": value = theunit != null && theunit.CreatureType == WoWCreatureType.Elemental ? 1 : 0; break;
                case "IsBeast": value = theunit != null && theunit.CreatureType == WoWCreatureType.Beast ? 1 : 0; break;
                case "IsCritter": value = theunit != null && theunit.CreatureType == WoWCreatureType.Critter ? 1 : 0; break;
                case "IsDragon": value = theunit != null && theunit.CreatureType == WoWCreatureType.Dragon ? 1 : 0; break;
                case "IsGasCloud": value = theunit != null && theunit.CreatureType == WoWCreatureType.GasCloud ? 1 : 0; break;
                case "IsGiant": value = theunit != null && theunit.CreatureType == WoWCreatureType.Giant ? 1 : 0; break;
                case "IsHumanoid": value = theunit != null && theunit.CreatureType == WoWCreatureType.Humanoid ? 1 : 0; break;
                case "IsMechanical": value = theunit != null && theunit.CreatureType == WoWCreatureType.Mechanical ? 1 : 0; break;
                case "IsNonCombatPet": value = theunit != null && theunit.CreatureType == WoWCreatureType.NonCombatPet ? 1 : 0; break;
                case "IsTotem": value = theunit != null && theunit.CreatureType == WoWCreatureType.Totem ? 1 : 0; break;
                case "IsUndead": value = theunit != null && theunit.CreatureType == WoWCreatureType.Undead ? 1 : 0; break;
                case "Health": value = theunit != null ? (int)theunit.HealthPercent : 100; break;
                case "Mana": value = theunit != null && (theunit.PowerType == WoWPowerType.Mana || theunit.Class == WoWClass.Druid) ? theunit.ManaPercent : 100; break;
                case "Mounted": value = theunit != null && (theunit.Mounted) ? 1 : 0; break;
                case "InBattleground": value = Battlegrounds.IsInsideBattleground ? 1 : 0; break;
                case "InParty": value = StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid ? 1 : 0; break;
                case "InRaid": value = StyxWoW.Me.GroupInfo.IsInRaid ? 1 : 0; break;
                case "IsDiseased": value = theunit != null && theunit.IsDiseased() ? 1 : 0; break;
                case "Dead": value = theunit != null && theunit.IsDead ? 1 : 0; break;
                case "IsCasting": value = theunit != null && (theunit.IsCasting || theunit.IsChanneling) ? 1 : 0; break;
                case "IsMoving": value = theunit != null && theunit.IsMoving ? 1 : 0; break;
                case "IsFlying": value = theunit != null && theunit.IsFlying ? 1 : 0; break;
                case "LineOfSight": value = theunit != null && theunit.InLineOfSight ? 1 : 0; break;
                case "Interruptable": value = theunit != null && theunit.CanInterruptCurrentSpellCast ? 1 : 0; break;
                case "IsElite": value = theunit != null && (theunit.Elite || theunit.Name.Contains("Training Dummy")) ? 1 : 0; break;
                case "IsBehind": value = theunit != null && theunit.CurrentTarget != null && theunit.CurrentTarget.MeIsSafelyBehind ? 1 : 0; break;
                case "IsFacingTarget": value = theunit != null && theunit.CurrentTarget != null && theunit.IsSafelyFacing(theunit.CurrentTarget) ? 1 : 0; break;
                case "IsFleeing": value = theunit != null && theunit.Fleeing ? 1 : 0; break;
                case "IsIncapacitated": value = theunit != null && (theunit.IsDead || theunit.IsCrowdControlled()) ? 1 : 0; break;
                case "IsRooted": value = theunit != null && theunit.HasAuraWithMechanic(WoWSpellMechanic.Rooted, WoWSpellMechanic.Shackled) ? 1 : 0; break;
                case "IsLooting": value = BotPoi.Current.Type == PoiType.Loot ? 1 : 0; break;
                case "PetIsMissing": value = StyxWoW.Me.PetIsMissing(); break;
                case "TotemCount": value = theunit != null ? FTWCoreStatus.TotemCount() : 0; break;
                case "RuneCount": value = StyxWoW.Me.DeathRuneCount +
                    StyxWoW.Me.FrostRuneCount +
                    StyxWoW.Me.BloodRuneCount +
                    StyxWoW.Me.UnholyRuneCount; break;
                case "DeathRune": value = StyxWoW.Me.DeathRuneCount; break;
                case "FrostRune": value = StyxWoW.Me.FrostRuneCount; break;
                case "BloodRune": value = StyxWoW.Me.BloodRuneCount; break;
                case "UnholyRune": value = StyxWoW.Me.UnholyRuneCount; break;
                case "RunicPower": value = StyxWoW.Me.RunicPowerPercent; break;
                case "LevelDiff": value = theunit != null ? theunit.Level - StyxWoW.Me.Level : 0; break;
                case "Level": value = theunit != null ? theunit.Level : 0; break;
                default: throw new Exception(string.Format("Unknown property {0}!", name));
            }
            return value;
        }

        private double GetAdvancedProperty(WoWUnit theunit, string prop_action, string prop)
        {
            string fnname = "FTWCore.Replace";
            MyTimer.Start(fnname);

            double value = 0;
            switch (prop_action)
            {
                case "Weapon1HasAura": value = (theunit != null && FTWCoreItems.WeaponEnchant(1) == prop) ? 1 : 0; break;
                case "Weapon2HasAura": value = (theunit != null && FTWCoreItems.WeaponEnchant(2) == prop) ? 1 : 0; break;
                case "AuraExpiring": value = (theunit != null) ? theunit.AuraExpiring(prop) : 0; break;
                case "CanCast": value = (theunit != null && SpellManager.CanCast(prop, theunit)) ? 1 : 0; break;
                case "ClearBehindMe": value = (theunit != null && StyxWoW.Me.ClearBehindMe(prop) ? 1 : 0); break;

                case "HasAura": value = (theunit != null && theunit.HasAura(prop)) ? 1 : 0; break;
                case "HasItem": value = StyxWoW.Me.HasItem(prop) ? 1 : 0; break;
                case "HasMyAura": value = (theunit != null && theunit.HasMyAura(prop)) ? 1 : 0; break;
                case "HasSpell": value = (StyxWoW.Me.HasSpell(prop)) ? 1 : 0; break;
                case "HasTotem": value = FTWCoreStatus.HasTotem(prop) ? 1 : 0; break;

                case "IsCasting": value = (theunit != null && theunit.CurrentSpellName() == prop) ? 1 : 0; break;
                case "ItemOnCooldown": value = FTWCoreItems.ItemOnCooldown(prop) ? 1 : 0; break;

                case "MyAuraExpiring": value = (theunit != null) ? theunit.MyAuraExpiring(prop) : 0; break;
                case "MyStackCount": value = (theunit != null) ? theunit.MyStackCount(prop) : 0; break;
                case "OnCooldown": value = FTWCoreStatus.OnCooldown(prop) ? 1 : 0; break;
                case "PartyWithAura": value = FTWCoreUnits.GetPartyWithAura(prop); break;


                case "PartyWithHealth": value = FTWCoreUnits.GetPartyWithHealth(prop); break;
                case "StackCount": value = (theunit != null) ? theunit.StackCount(prop) : 0; break;
                case "NumItems": value = (theunit != null) ? FTWCoreItems.NumItems(prop) : 0; break;
                default: throw new Exception(string.Format("Unknown action {0}!", prop_action));
            }
            // Stop the timer
            MyTimer.Stop(fnname);

            return value;
        }

        private void setEclipseDirection()
        {
            if (StyxWoW.Me.HasAura("Eclipse (Solar)"))
                FTWProps._eclipsedirection = -1;
            else if (StyxWoW.Me.HasAura("Eclipse (Lunar)"))
                FTWProps._eclipsedirection = 1;
        }
    }
}
